zerochainの実装にキャッチアップする (1) confidential_transfer
TL; DR
moonty_sal.iconがzerochainの開発に入るための準備として書いているメモです
Substrate的なやつ
code: conf_transfer.rs
// substrate的なStoreはこちら
decl_storage! {
trait Store for Module<T: Trait> as ConfTransfer {
// The encrypted balance for each account
pub EncryptedBalance get(encrypted_balance) config() : map PkdAddress => Option<Ciphertext>;
// The verification key of zk proofs (only readable)
pub VerifyingKey get(verifying_key) config(): PreparedVk;
}
}
各ユーザに対応する暗号化された残高がここに入っている。(pldAddress => Option<CipherText>)
optionalなのでmatch文を使って値自体を取得するなどする
code:confidential_transfer.rs
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
// Initializing events
// this is needed only if you are using events in your module
fn deposit_event<T>() = default;
pub fn confidential_transfer(
_origin,
zkproof: Proof,
address_sender: PkdAddress,
address_recipient: PkdAddress,
value_sender: Ciphertext,
value_recipient: Ciphertext,
rk: SigVerificationKey, // TODO: Extract from origin
fee_sender: Ciphertext
) -> Result {
// let rk = ensure_signed(origin)?;
// Get zkproofs with the type
let szkproof = match zkproof.into_proof() {
Some(v) => v,
None => return Err("Invalid zkproof"),
};
// Get address_sender with the type
let saddr_sender = match address_sender.into_encryption_key() {
Some(v) => v,
None => return Err("Invalid address_sender"),
};
// Get address_recipient with the type
let saddr_recipient = match address_recipient.into_encryption_key() {
Some(v) => v,
None => return Err("Invalid address_recipient"),
};
// Get value_sender with the type
let svalue_sender = match value_sender.into_ciphertext() {
Some(v) => v,
None => return Err("Invalid value_sender"),
};
// Get value_recipient with the type
let svalue_recipient = match value_recipient.into_ciphertext() {
Some(v) => v,
None => return Err("Invalid value_recipient"),
};
// Get fee_sender with the type
let sfee_sender = match fee_sender.into_ciphertext() {
Some(v) => v,
None => return Err("Invalid fee_sender"),
};
// Get rk with the type
let srk = match rk.into_verification_key() {
Some(v) => v,
None => return Err("Invalid rk"),
};
// Get balance_sender with the type
let bal_sender = match Self::encrypted_balance(address_sender) {
Some(b) => match b.into_ciphertext() {
Some(c) => c,
None => return Err("Invalid ciphertext of sender balance"),
},
None => return Err("Invalid sender balance"),
};
// Verify the zk proof
ensure!(
Self::validate_proof(
&szkproof,
&saddr_sender,
&saddr_recipient,
&svalue_sender,
&svalue_recipient,
&bal_sender,
&srk,
&sfee_sender,
),
"Invalid zkproof"
);
// Get balance_recipient with the option type
let bal_recipient = match Self::encrypted_balance(address_recipient) {
Some(b) => b.into_ciphertext(),
_ => None
};
// Charge transaction fee on the sender's balance
<EncryptedBalance<T>>::mutate(address_sender, |balance| {
let new_balance = balance.clone().map(
|_| Ciphertext::from_ciphertext(&bal_sender.sub_no_params(&sfee_sender)));
*balance = new_balance
});
// Update the sender's balance
<EncryptedBalance<T>>::mutate(address_sender, |balance| {
let new_balance = balance.clone().map(
|_| Ciphertext::from_ciphertext(&bal_sender.sub_no_params(&svalue_sender)));
*balance = new_balance
});
// Update the recipient's balance
<EncryptedBalance<T>>::mutate(address_recipient, |balance| {
let new_balance = balance.clone().map_or(
Some(Ciphertext::from_ciphertext(&svalue_recipient)),
|_| Some(Ciphertext::from_ciphertext(&bal_recipient.unwrap().add_no_params(&svalue_recipient)))
);
*balance = new_balance
});
// TODO: tempolaly removed address_sender and address_recipient because of mismatched types
Self::deposit_event(RawEvent::ConfTransfer(zkproof, value_sender, value_recipient, Ciphertext::from_ciphertext(&bal_sender), rk));
Ok(())
}
}
}
と言ってもモジュールが大量にあるの一歩一歩理解を深めていくしかないのだだ
まずは引数の理解から
code: argument.rs
_origin,
zkproof: Proof, // 証明
address_sender: PkdAddress, // 送信者のアドレス
address_recipient: PkdAddress, // 受信者のアドレス
value_sender: Ciphertext,
value_recipient: Ciphertext,
rk: SigVerificationKey, // TODO: Extract from origin
fee_sender: Ciphertext // 手数料
Proof型
core/primitives/src/proof.rs に置かれている。
code: proof.rs
pub struct Proof(pub Vec<u8>); // u8のVec (8bitのInterger, バイト列で表現されているのだろう)
impl Proof {
// bellman verifierのproofへ変換する君
pub fn into_proof(&self) -> Option<bellman_verifier::Proof<Bls12>> {
bellman_verifier::Proof::<Bls12>::read(&self.0..).ok() }
// from_proof 逆にbellman verifierからProofを生成するくん
pub fn from_proof(proof: &bellman_verifier::Proof<Bls12>) -> Self {
proof.write(&mut &mut writer..).unwrap(); Proof(writer.to_vec())
}
}
PkdAddress型
core/primitives/src/pkd_address.rsに置かれている
Simple 256 bit (32 bytes) hash type.を生成してくれる君を利用している
code: pkd_address.rs
const SIZE: usize = 32;
construct_fixed_hash! {
pub struct H256(SIZE);
}
pub type PkdAddress = H256;
// Deserialize, Serializeなどは実装されている
impl PkdAddress {
pub fn into_encryption_key(&self) -> Option<EncryptionKey<Bls12>> {
EncryptionKey::<Bls12>::read(&mut &self.0.., &JUBJUB).ok() }
pub fn from_encryption_key(address: &EncryptionKey<Bls12>) -> Self {
address.write(&mut writer..).unwrap(); H256::from_slice(&writer)
}
}
Ciphertext型
core/primitives/src/ciphertext.rs に置かれている
code: ciphertext.rs
impl Ciphertext {
pub fn into_ciphertext(&self) -> Option<elgamal::Ciphertext<Bls12>> {
elgamal::Ciphertext::read(&mut &self.0.., &JUBJUB as &JubjubBls12).ok() }
pub fn from_ciphertext(ciphertext: &elgamal::Ciphertext<Bls12>) -> Self {
ciphertext.write(&mut writer..).unwrap(); Ciphertext(writer.to_vec())
}
}
SigVerificationKey 型
code: sig_vk.rs
construct_fixed_hash! {
pub struct H256(SIZE);
}
pub type SigVerificationKey = H256;
// (omit some parts)
impl SigVerificationKey {
pub fn into_verification_key(&self) -> Option<redjubjub::PublicKey<Bls12>> {
redjubjub::PublicKey::read(&mut &self.0.., &JUBJUB as &JubjubBls12).ok() }
pub fn from_verification_key(vk: &redjubjub::PublicKey<Bls12>) -> Self {
vk.write(&mut &mut writer..).unwrap(); H256::from_slice(&writer)
}
}
これもPkdAddress型と同じように construct_fixed_hash!マクロを利用して作られている
redjubjub::PublicKey<Bls12>とな...
jubjubの語源は鳥らしいので赤い鳥なのかな...
Option<redjubjub::PublicKey<Bls12>> この型が出てくるけれど?
//! Implementation of RedJubjub, a specialization of RedDSA to the Jubjub curve.
これに関しては RedDSAという署名らしい (Saplingに使われている)シュノア署名の亜種のようだ。
RedDSA is a Schnorr-based signature scheme, optionally supporting key re-randomization as described in §4.1.6.1 ‘Signature with Re-Randomizable Keys’ on p. 22. It also supports a Secret Key to Public Key Homomorphism as described in §4.1.6.2 ‘Signature with Private Key to Public Key Homomorphism’ on p. 23. It is based on a scheme from FKMSSS2016, section 3, with some ideas from EdDSA BJLSY2015. 処理を簡単に追う
let szkproof = match zkproof.into_proof() {
proofをbellman_verifier::Proof<Bls12>型に変換する
let saddr_sender = match address_sender.into_encryption_key() {
H256 -> EncryptionKey<Bls12>へ変換
let saddr_recipient = match address_recipient.into_encryption_key()
アドレスを暗号化鍵に変換
let svalue_sender = match value_sender.into_ciphertext() {
let svalue_recipient = match value_recipient.into_ciphertext() {
let sfee_sender = match fee_sender.into_ciphertext() {
elgamal::Ciphertext<Bls12>
Lifted El Gamal暗号の型に変換する
Fee / 送信者のXXX / 受信者のXXX
let srk = match rk.into_verification_key() {
let bal_sender = match Self::encrypted_balance(address_sender) {
送信者の残高をチェックする?
validate_proof
code:conf_transfer.rs
ensure!(
Self::validate_proof(
&szkproof,
&saddr_sender,
&saddr_recipient,
&svalue_sender,
&svalue_recipient,
&bal_sender,
&srk,
&sfee_sender,
),
"Invalid zkproof"
);
validate_rpoodしているところを見る
code:conf_transfer.rs
pub fn validate_proof (
zkproof: &bellman_verifier::Proof<Bls12>,
address_sender: &EncryptionKey<Bls12>,
address_recipient: &EncryptionKey<Bls12>,
value_sender: &elgamal::Ciphertext<Bls12>,
value_recipient: &elgamal::Ciphertext<Bls12>,
balance_sender: &elgamal::Ciphertext<Bls12>,
rk: &PublicKey<Bls12>,
fee_sender: &elgamal::Ciphertext<Bls12>
) -> bool {
各点をxyに変換していってpublic inputを生成していく。
into_xy (アフィン空間で考えて、x,y座標を得る) -> エドワーズ曲線 code:rs
pub fn into_xy(&self) -> (E::Fr, E::Fr)
{
let zinv = self.z.inverse().unwrap();
let mut x = self.x;
x.mul_assign(&zinv);
let mut y = self.y;
y.mul_assign(&zinv);
(x, y)
}
verifiy_proodしている部分を見る
zero-chain/core/bellman-verifier/src/verifier.rs
code: verifier.rs
pub fn verify_proof<'a, E: Engine>(
pvk: &'a PreparedVerifyingKey<E>,
proof: &Proof<E>,
) -> Result<bool, SynthesisError>
{
if (public_inputs.len() + 1) != pvk.ic.len() {
return Err(SynthesisError::MalformedVerifyingKey);
}
let mut acc = pvk.ic0.into_projective(); for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) {
acc.add_assign(&b.mul(i.into_repr()));
}
// The original verification equation is:
// A * B = alpha * beta + inputs * gamma + C * delta
// ... however, we rearrange it so that it is:
// A * B - inputs * gamma - C * delta = alpha * beta
// or equivalently:
// A * B + inputs * (-gamma) + C * (-delta) = alpha * beta
// which allows us to do a single final exponentiation.
Ok(E::final_exponentiation(
&E::miller_loop([
(&proof.a.prepare(), &proof.b.prepare()),
(&acc.into_affine().prepare(), &pvk.neg_gamma_g2),
(&proof.c.prepare(), &pvk.neg_delta_g2)
].into_iter())
).unwrap() == pvk.alpha_g1_beta_g2)
}
verify_proofの後続部分(proofが検証されたあとの処理)
受信者の暗号化された残高を取得
code: conf_transfer.rs
// Get balance_recipient with the option type
let bal_recipient = match Self::encrypted_balance(address_recipient) {
Some(b) => b.into_ciphertext(),
_ => None
};
送信者の残高からfeeを引く (sub_no_paramsはhomomorphicな計算)
code: conf_transfer.rs
// Charge transaction fee on the sender's balance
<EncryptedBalance<T>>::mutate(address_sender, |balance| {
let new_balance = balance.clone().map(
|_| Ciphertext::from_ciphertext(&bal_sender.sub_no_params(&sfee_sender)));
*balance = new_balance
});
送信者の残高を更新(feeもverificationに含まれているから 元の残高 - fee - 送金額 < 0 になることはない?
code: conf_transfer.rs
// Update the sender's balance
<EncryptedBalance<T>>::mutate(address_sender, |balance| {
let new_balance = balance.clone().map(
|_| Ciphertext::from_ciphertext(&bal_sender.sub_no_params(&svalue_sender)));
*balance = new_balance
});
受信者の残高を更新
code: conf_tranfer.rs
// Update the recipient's balance
<EncryptedBalance<T>>::mutate(address_recipient, |balance| {
let new_balance = balance.clone().map_or(
Some(Ciphertext::from_ciphertext(&svalue_recipient)),
|_| Some(Ciphertext::from_ciphertext(&bal_recipient.unwrap().add_no_params(&svalue_recipient)))
);
*balance = new_balance
});
code: conf_transfer..rs
// TODO: tempolaly removed address_sender and address_recipient because of mismatched types
Self::deposit_event(RawEvent::ConfTransfer(zkproof, value_sender, value_recipient, Ciphertext::from_ciphertext(&bal_sender), rk));
Rustのメモ
tuple structで要素が1つの場合の表記
暗号学的背景
続き